<!DOCTYPE html>
<html>
  <head>
    <title>
      Test Processing Of AudioParams of Disconnected AudioNodes
    </title>
    <script src="../../resources/testharness.js"></script>
    <script src="../../resources/testharnessreport.js"></script>
    <script src="../resources/audit.js"></script>
  </head>
  <body>
    <script id="layout-test-code">
      // Arbitrary sample rate.
      let sampleRate = 8000;

      // Arbitrary number of frames for the test, except it must be greater than
      // |automationEndFrame|.
      let renderFrames = 1024;

      // The linear ramp automation ends at this frame. Arbitrary, except it
      // must be strictly less than the render length.
      let automationEndFrame = 128;

      let audit = Audit.createTaskRunner();

      // There should be a test for every single node which has an AudioParam.
      // Source nodes are not included in this because the AudioParams for the
      // source nodes only process when the node has been started.

      audit.define('BiquadFilterNode', (task, should) => {
        let nodeName = 'BiquadFilterNode'
        Promise
            .all([
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'Q',
                initialValue: 2,
                rampFinalValue: 5
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'detune',
                initialValue: 1,
                rampFinalValue: 0.5
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'frequency',
                initialValue: 1000,
                rampFinalValue: 100
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'gain',
                initialValue: -3,
                rampFinalValue: 3
              }),
            ])
            .then(() => task.done());
      });

      audit.define('DelayNode', (task, should) => {
        testParamAutomation(should, {
          nodeName: 'DelayNode',
          paramName: 'delayTime',
          initialValue: 0.25,
          rampFinalValue: 0.5
        }).then(() => task.done());
      });

      audit.define('DynamicsCompressorNode', (task, should) => {
        let nodeName = 'DynamicsCompressorNode';
        Promise
            .all([
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'attack',
                initialValue: 0.1,
                rampFinalValue: 0.5
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'knee',
                initialValue: 0,
                rampFinalValue: 25
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'ratio',
                initialValue: 1,
                rampFinalValue: 15
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'release',
                initialValue: 0,
                rampFinalValue: 0.75
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'threshold',
                initialValue: -50,
                rampFinalValue: -10
              })
            ])
            .then(() => task.done());
      });

      audit.define('GainNode', (task, should) => {
        testParamAutomation(should, {
          nodeName: 'GainNode',
          paramName: 'gain',
          initialValue: 1,
          rampFinalValue: 0.5
        }).then(() => task.done());
      });

      audit.define('PannerNode', (task, should) => {
        let nodeName = 'PannerNode';
        Promise
            .all([
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'positionX',
                initialValue: 0.1,
                rampFinalValue: 0.5
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'positionY',
                initialValue: 2,
                rampFinalValue: 30
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'positionZ',
                initialValue: 1,
                rampFinalValue: 15
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'orientationX',
                initialValue: 0.1,
                rampFinalValue: 0.5
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'orientationY',
                initialValue: 2,
                rampFinalValue: 30
              }),
              testParamAutomation(should, {
                nodeName: nodeName,
                paramName: 'orientationZ',
                initialValue: 1,
                rampFinalValue: 15
              }),
            ])
            .then(() => task.done());
      });

      audit.define('StereoPannerNode', (task, should) => {
        testParamAutomation(should, {
          nodeName: 'StereoPannerNode',
          paramName: 'pan',
          initialValue: 1,
          rampFinalValue: 0.5
        }).then(() => task.done());
      });

      audit.define('AudioListener', (task, should) => {
        Promise
            .all([
              testAudioListener(should, {
                paramName: 'positionX',
                initialValue: 1,
                rampFinalValue: 100
              }),
              testAudioListener(should, {
                paramName: 'positionY',
                initialValue: 1,
                rampFinalValue: 200
              }),
              testAudioListener(should, {
                paramName: 'positionZ',
                initialValue: 1,
                rampFinalValue: 300
              }),
              testAudioListener(should, {
                paramName: 'forwardX',
                initialValue: 1,
                rampFinalValue: -100
              }),
              testAudioListener(should, {
                paramName: 'forwardY',
                initialValue: 1,
                rampFinalValue: -200
              }),
              testAudioListener(should, {
                paramName: 'forwardZ',
                initialValue: 1,
                rampFinalValue: -300
              }),
              testAudioListener(
                  should,
                  {paramName: 'upX', initialValue: 1, rampFinalValue: 99}),
              testAudioListener(
                  should,
                  {paramName: 'upY', initialValue: 1, rampFinalValue: 42}),
              testAudioListener(
                  should,
                  {paramName: 'upZ', initialValue: 1, rampFinalValue: 137}),
            ])
            .then(() => task.done());
      });

      // Run test of automation processing. |options| is a dictionary that
      // describes the node to test, the AudioParams to be tested and the values
      // for the AudioParam.  The members of the dictionary are:
      //
      //   nodeName       - name of the node (constructor name)
      //   paramName      - name of the AudioParam to be tested
      //   initialValue   - starting value for linear ramp
      //   rampFinalValue - finale value for linear ramp
      //
      // The test is considered to have succeeded if the |.value| of the
      // AudioParam is the final value.
      //
      // A simple graph is created containing the node to be tested, connected
      // to the destination.  A linear ramp automation is scheduled for the
      // specified AudioParam, starting and ending at the values given.
      //
      // Processing is started and after some number of frames, the |.value| of
      // the AudioParam is obtained and compared against the final value.  These
      // should match exactly.
      function testParamAutomation(should, options) {
        let context = new OfflineAudioContext(1, renderFrames, sampleRate);

        // Create the node to be tested
        let node = new window[options.nodeName](context);
        node.connect(context.destination);

        // A linear ramp starting at frame 0 to frame |automationEndFrame| is
        // used for the test.  This value is fairly arbitrary, but it should be
        // less than the total render frames.

        node[options.paramName].setValueAtTime(options.initialValue, 0);
        node[options.paramName].linearRampToValueAtTime(
            options.rampFinalValue, automationEndFrame / context.sampleRate);

        return context.startRendering().then(function(resultBuffer) {
          // Sanity check: the given ramp final value must not be the default
          // value, otherwise we can't easily tell if the automation was
          // actually run.
          should(
              options.rampFinalValue,
              options.nodeName + '.' + options.paramName + ' ramp final value')
              .notBeEqualTo(node[options.paramName].defaultValue);

          // The actual AudioParam value should be the rampe final value.
          should(
              node[options.paramName].value,
              options.nodeName + '.' + options.paramName + '.value')
              .beEqualTo(options.rampFinalValue);
        });
      }

      function testAudioListener(should, options) {
        let context = new OfflineAudioContext(1, renderFrames, sampleRate);

        // Create the node to be tested
        let node = new PannerNode(context);
        node.connect(context.destination);

        // A linear ramp starting at frame 0 to frame |automationEndFrame| is
        // used for the test.  This value is fairly arbitrary, but it should be
        // less than the total render frames.

        context.listener[options.paramName].setValueAtTime(
            options.initialValue, 0);
        context.listener[options.paramName].linearRampToValueAtTime(
            options.rampFinalValue, automationEndFrame / context.sampleRate);

        return context.startRendering().then(function(resultBuffer) {
          // Sanity check: the given ramp final value must not be the default
          // value, otherwise we can't easily tell if the automation was
          // actually run.
          should(
              options.rampFinalValue,
              'AudioListener.' + options.paramName + ' ramp final value')
              .notBeEqualTo(context.listener[options.paramName].defaultValue);

          // The actual AudioParam value should be the rampe final value.
          should(
              context.listener[options.paramName].value,
              'AudioListener.' + options.paramName + '.value')
              .beEqualTo(options.rampFinalValue);
        });
      }

      audit.run();
    </script>
  </body>
</html>
